/**
 * @brief 3.4.1 使用迭代器
 * 所有标准库容器都可以使用迭代器，但是其中只有少数几种才同时支持下标运算符。
 * 类似于指针类型（参见2.3.2节，第47页），迭代器也提供了对对象的间接访问。
 * 使用迭代器可以访问某个元素，迭代器也能从一个元素移动到另外一个元素。迭代器有有效和无效之分，这一点和指针差不多。
 * 有效的迭代器或者指向某个元素，或者指向容器中尾元素的下一位置；其他所有情况都属于无效。
 * @param argc 
 * @param argv 
 * @return int 
 */
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
using std::cin, std::cout, std::endl;
using std::string;
using std::vector;
using std::iterator;
int main(int argc, char const *argv[])
{
    // 有迭代器的类型同时拥有返回迭代器的成员
    // 比如，这些类型都拥有名为begin和end的成员，其中begin成员负责返回指向第一个元素（或第一个字符）的迭代器
    // end成员返回的迭代器常被称作尾后迭代器（off-the-end iterator）或者简称为尾迭代器（end iterator）。
    // 特殊情况下如果容器为空，则begin和end返回的是同一个迭代器。

    // 第一个字母改成大写
    string s("some string");
    if(s.begin() != s.end()) // 确保s不为空
    {
        auto it = s.begin(); // it指向s的第一个字符
        *it = toupper(*it); // 将当前字符改成大写形式
    }
    cout << s << endl;

    // 第一个单词改成大写
    for(auto it = s.begin(); it != s.end() && !isspace(*it); ++it)
    {
        *it = toupper(*it);
    }
    cout << s << endl;

    /**
     * @brief 只有string和vector等一些标准库类型有下标运算符，而并非全都如此。
     * 与之类似，所有标准库容器的迭代器都定义了==和！=，但是它们中的大多数都没有定义<运算符。
     * 因此，只要我们养成使用迭代器和！=的习惯，就不用太在意用的到底是哪种容器类型。
     */

    // 迭代器类型有iterator和const_iterator两种
    vector<int>::iterator it1; // it1能读写vector&lt;int&gt;的元素
    string::iterator it2; // it2能读写string对象中的字符
    vector<int>::const_iterator it3; // it3只能读元素，不能写元素
    string::const_iterator it4; // it4只能读字符，不能写字符

    // 如果vector对象或string对象是一个常量，只能使用const_iterator；
    // 如果vector对象或string对象不是常量，那么既能使用iterator也能使用const_iterator。
    vector<int> v;
    const vector<int> cv;
    auto it11 = v.begin(); // it11类型为vector<int>::iterator
    auto it12 = cv.begin(); // it12类型为vector<int>::const_iterator

    // 如果对象只需读操作而无须写操作的话最好使用常量类型（比如const_iterator）。
    // 为了便于专门得到const_iterator类型的返回值，C++11新标准引入了两个新函数，分别是cbegin和cend
    auto it13 = v.cbegin(); // it13类型为vector<int>::const_iterator

    // 结合解引用和成员访问操作
    // 注意，(*it).empty()中的圆括号必不可少
    vector<string> svec{"1","a","3"};
    auto sit = svec.begin();
    bool isEmpty = (*sit).empty(); // 必须对解引操作加上圆括号
    //bool isEmpty = *sit.empty(); // 错误，试图访问迭代器it的empty()成员，但it没有该成员

    // 箭头运算符 ->
    // 把解引用和成员访问两个操作结合在一起
    bool isEmpty1 = sit->empty(); // sit->empty()等价于(*sit).empty()

    // 谨记，但凡是使用了迭代器的循环体，都不要向迭代器所属的容器添加元素。


    return 0;
}
